package com.lfs.dispatcher.charge.service.impl;

import com.lfs.common.constant.CommonConstants;
import com.lfs.dispatcher.charge.dao.ApiDisChargeDao;
import com.lfs.dispatcher.charge.service.ApiDisChargeService;
import com.lfs.dispatcher.feign.AdapterFeignService;
import com.lfs.dispatcher.util.JacksonUtils;
import com.lfs.interfaces.dto.ResultReturn;
import com.lfs.interfaces.model.NotifyModel;
import com.lfs.interfaces.model.dto.ChargeDto;
import com.lfs.interfaces.model.dto.OrderDto;
import com.lfs.interfaces.model.vo.ChargeVo;
import com.lfs.interfaces.model.vo.ProductVo;
import com.lfs.interfaces.order.service.OrderBaseService;
import com.lfs.interfaces.product.service.ProductService;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.SocketTimeoutException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class ApiDisChargeServiceImpl implements ApiDisChargeService {

    private static final Logger log = LoggerFactory.getLogger(ApiDisChargeServiceImpl.class);

    @Value("${submitOrderUrl}")
    private String submitOrderUrl;

    @Value("${notifyAdapterUrl}")
    private String notifyAdapterUrl;

    @Autowired
    private AdapterFeignService adapterFeignService;

    @Resource
    private ApiDisChargeDao apiDisChargeDao;

    @Resource
    private ProductService productService;

    @Resource
    private OrderBaseService orderBaseService;

    @Override
    public void charge(OrderDto orderDto) {
        ResultReturn<ChargeVo> resultReturn = new ResultReturn<ChargeVo>();
        ChargeDto chargeDto = new ChargeDto();
        chargeDto.setAgtPhone(orderDto.getAgtPhone());
        chargeDto.setChargeAddr(orderDto.getChargeAddr());
        chargeDto.setChargeMoney(orderDto.getChargeMoney());
        chargeDto.setExp(null);
        chargeDto.setNotifyUrl(null);
        chargeDto.setProductCode(String.valueOf(orderDto.getProductCode()));
        chargeDto.setBusinessCode(orderDto.getBusinessCode());
        chargeDto.setReqStreamId(orderDto.getDownOrderNo());
        chargeDto.setProductType(orderDto.getProductType());
        chargeDto.setTradePwd(null);
        orderDto.setRechargeType("");
        List<Map<String, Object>> listProducts = null;

        /**
         * 获取价格最低、权重最高的通道产品
         */
        listProducts = productService.getAdapter(chargeDto);

        if (listProducts.size() > 0) {
            try {
                Map<String, Object> map = listProducts.get(0);
                if (listProducts.size() > 1) {
                    map = dealWeight(map, listProducts);
                }

                orderDto.setAdapterName((String) map.get("adapter_name"));
                // 校验产品信息
                ResultReturn<ProductVo> resultProduct = productService.sendVerifyOrder(chargeDto);
                if (resultProduct.getStatus().equals(CommonConstants.RESP_CHARGE_SUCCESS)) {
                    log.info("请求流水号[" + chargeDto.getReqStreamId() + "]选择供应商[" + map.get("channel_num") + "]");
                    String endPrice = String.valueOf(map.get("end_price"));
                    String channelCost = String.valueOf(map.get("product_cost"));
                    BigDecimal channelPrice = new BigDecimal(endPrice).setScale(2, BigDecimal.ROUND_HALF_UP);
                    orderDto.setUpchanel(String.valueOf(map.get("channel_num")));
                    orderDto.setChannelPrice(channelPrice == null ? new BigDecimal(0.00) : channelPrice);
                    orderBaseService.updateChannelPrice(orderDto);
                    resultReturn=  adapterFeignService.submitOrder(orderDto);

                    log.info("【上游返回信息】,result={},orderId={}", resultReturn, orderDto.getOrderNo());
                    String msg = resultReturn.getOrder() == null ? resultReturn.getMsg() : resultReturn.getOrder().getErrorCode();
                    if (resultReturn != null && resultReturn.getStatus().equals(CommonConstants.RESP_CHARGE_HANDLE)) {
                        log.info("上游返回成功，订单状态改为正在处理中,msg={}", resultReturn.getMsg());
                        orderDto.setErrorCode(CommonConstants.RESP_CHARGE_HANDLE.toString());
                        orderDto.setRemark(resultReturn.getMsg());
                        orderDto.setUpOrderNo((resultReturn.getOrder() != null ? resultReturn.getOrder().getUpOrderNo() : ""));
                        orderDto.setQrCode(resultReturn.getOrder().getQrCode());
                        //更改订单通道状态
                        orderBaseService.updateChannelStatus(orderDto);
                    } else {
                        //调用上游失败
                        orderDto.setErrorCode(CommonConstants.RESP_CHARGE_FAIL.toString());
                        orderDto.setRemark(msg);
                        orderDto.setChargeAddr(resultReturn.getOrder().getChargeAddr());
                        orderBaseService.updateChannelStatus(orderDto);
                        dealFail(orderDto);
                    }
                } else {
                    orderDto.setErrorCode(CommonConstants.RESP_CHARGE_VALIDBUSINESS.toString());
                    orderDto.setRemark("产品校验失败");
                    dealFail(orderDto);
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                orderDto.setErrorCode(CommonConstants.RESP_CHARGE_HANDLE.toString());
                orderDto.setRemark("订单提交异常");
            }
        } else {
            orderDto.setErrorCode(CommonConstants.RESP_CHARGE_VALIDBUSINESS.toString());
            orderDto.setRemark("未找到通道");
            //失败处理
            dealFail(orderDto);
        }
    }

    //根据权重选通道

    public Map<String, Object> dealWeight(Map<String, Object> map, List<Map<String, Object>> listProducts) {
        log.info("【通道选择】,获取通道产品个数={}", listProducts.size());
        List<Map<String, Object>> listM = new ArrayList<Map<String, Object>>();
        //获取最低价   获取通道按照通道价格升序排列
        BigDecimal lowerPrice = (BigDecimal) listProducts.get(0).get("end_price");
        //循环通道产品,将前面的权重累加  如  1通道 (0,10] 2通道 (10,20]  3通道  (20,110]   在110中取任一随机数    随机数在哪个区间就取哪个通道
        for (int j = 0; j < listProducts.size(); j++) {
            Map<String, Object> m = listProducts.get(j);
            if (lowerPrice.compareTo((BigDecimal) m.get("end_price")) == 0) {
                if (j > 0) {
                    m.put("weight", (int) listM.get(j - 1).get("weight") + (int) m.get("weight"));
                    //此次累加前的值
                    m.put("before", listM.get(j - 1).get("weight"));
                } else {
                    m.put("before", 0);
                }
                listM.add(m);
            }
        }
        log.info("【通道选择】,获取到价格最低的通道个数为={}", listM.size());
        if (listM.size() > 1) {
            //获取weight 累加后的最大值
            int max = (int) listM.get(listM.size() - 1).get("weight");
            //生成最大值以内的随机数
            int result = new Random().nextInt(max);
            //获取随机数所在的通道
            for (int i = 0; i < listM.size(); i++) {
                //落在区间内
                if ((int) listM.get(i).get("before") < result && result <= (int) listM.get(i).get("weight")) {
                    map = listM.get(i);
                    break;
                }
            }
        } else {
            map = listM.get(0);
        }
        return map;
    }


    public void dealFail(OrderDto orderDto) {
        orderDto.setState(1);
        orderDto.setAccountBalance(new BigDecimal(0));
        orderDto.setUpOrderNo("");
        orderDto.setCheckState(1);
        orderDto.setChargeAddr(orderDto.getChargeAddr());
        orderBaseService.updateOrderAfterCharge(orderDto);
        adapterFeignService.notify(new NotifyModel(orderDto.getOrderNo(), CommonConstants.DOWN_STREAM, orderDto.getUpOrderNo()));
//        //回调下游
////        HttpSendUtil.sendPost(, notifyAdapterUrl);
    }


    private String sendGameRec(OrderDto order) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(600000)
                .setConnectTimeout(600000).setConnectionRequestTimeout(600000).build();//设置请求(10分钟)和传输超时时间(10分钟)
        String url = submitOrderUrl;
        HttpPost httpPost = new HttpPost(url);
        String paramsStr = JacksonUtils.writeValue(order);
        httpPost.setEntity(new StringEntity(paramsStr, "utf-8"));
        httpPost.addHeader("Content-Type", "application/json;charset=UTF-8");
        httpPost.addHeader("Accept", "application/json");
        httpPost.setConfig(requestConfig);
        try {
            CloseableHttpResponse response = httpClient.execute(httpPost);
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                HttpEntity entity = response.getEntity();
                return EntityUtils.toString(entity);
            } else {
                log.error("请求：" + url + "，失败，返回码:" + response.getStatusLine().getStatusCode());
            }
        } catch (SocketTimeoutException e1) {
            log.error("请求：" + url + "，超时，e={}", e1);
        } catch (Exception e) {
            log.error("请求：" + url + "，失败，e={}", e);
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                log.error("关闭httpClient失败，e={}", e);
            }
        }
        return null;
    }

    public Map<String, Object> checkState(OrderDto order) {
        return apiDisChargeDao.checkState(order.getOrderNo());
    }


    /**
     * 判断 第一次提交时间与当前时间，相差超过半小时则直接失败不在提交
     *
     * @param applyTime
     * @return
     */
    private boolean validateTime(String applyTime) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            Long applyLong = formatter.parse(applyTime).getTime();
            Long curLong = new Date().getTime();
            if ((curLong - applyLong) / 1000 > 1800) {
                return true;
            }
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return false;
    }


    /**
     * 获取没有提单的通道
     *
     * @param listProducts
     * @param chanelList
     * @return
     */
    private List<Map<String, Object>> delChannel(List<Map<String, Object>> listProducts,
                                                 List<String> chanelList) {

        List<Map<String, Object>> re = new ArrayList<Map<String, Object>>();
        for (int i = 0; i < listProducts.size(); i++) {
            Map<String, Object> amap = listProducts.get(i);
            String channelNum = amap.get("channel_num").toString();

            if (!chanelList.contains(channelNum)) {
                re.add(amap);
            }
        }
        return re;
    }

}
